package lumis.portal.htmlevaluation;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.rmi.UnexpectedException;
import java.util.ArrayList;
import java.util.List;

import lumis.portal.htmlevaluation.HtmlEvaluationMessage.Level;

import org.w3c.tidy.Tidy;

/**
 * <p>
 * {@link #IHtmlEvaluationManager} implementation that allows to evaluate an URL
 * or HTML content according with the accessibility roles.
 * </p>
 * 
 * <p>
 * For evaluation of an URL, its possible evaluate, both External URL and Portal
 * URL.<br>
 * For Portal URL, is necessary to inform the user session ID.
 * </p>
 * 
 * @version $Revision: 13093 $ $Date: 2011-05-28 18:40:18 -0300 (Sat, 28 May 2011) $
 * @since 6.0.0
 */
public class HtmlEvaluationManager implements IHtmlEvaluationManager
{
	/**
	 * Makes the Evaluation for a specific URL. If the URL is an external URL,
	 * the <code>userSessionId</code> can be null. Otherwise, the userSessionId
	 * must be informed.
	 * 
	 * @param level
	 *            Minimal level to be considered.
	 * @param url
	 *            URL to be evaluated.
	 * @param userSessionId
	 *            user session ID. This information can be null if the URL is a
	 *            external URL.
	 * @return list of messages founded for the URL.
	 * @since 6.0.0
	 */
	public List<HtmlEvaluationMessage> evaluate(Level level, URL url, String userSessionId) throws UnexpectedException
	{
		try
		{
			// Creates the HttpURLConnection to receive the InputStream
			HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
			httpConn.setConnectTimeout(30000);
			httpConn.setRequestMethod("GET");
			// if the userSessionId is null means that is a external url.
			// otherwise, is a portal url. In this case must be set the cookie
			// with the userSessionId.
			if (userSessionId != null)
			{
				httpConn.setRequestProperty("cookie", "lumUserSessionId=" + userSessionId);
			}
			httpConn.connect();
			// receives the Input Stream.
			InputStream is = httpConn.getInputStream();

			// Creates the tidy object that will be evaluate the content.
			Tidy tidy = new Tidy();
			// sets the listener that will be store the messages founded for
			// the content.
			HtmlEvaluationTidyMessageListener listener = new HtmlEvaluationTidyMessageListener();
			tidy.setMessageListener(listener);
			tidy.setInputEncoding("UTF-8");
			// creates a fake output to avoid that the messages found, for the
			// tidy, are printed on console.
			OutputStream os = new OutputStream()
			{
				StringBuilder sb = new StringBuilder();

				@Override
				public void write(int b) throws IOException
				{
					sb.append(b);
				}
			};
			PrintWriter output = new PrintWriter(os);
			// sets the fake output
			tidy.setErrout(output);
			// makes the evaluation
			tidy.parse(is, new StringWriter());
			// extract and return the messages found for the content.
			return extractResultMessages(level, listener);
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e.getMessage(), e);
		}
	}

	/**
	 * Makes the Evaluation for a HTML content passed as parameter.
	 * 
	 * @param level
	 *            Minimal level to be considered.
	 * @param html
	 *            HTML content to be evaluated.
	 * @return list of messages founded for the URL.
	 * @since 6.0.0
	 */
	public List<HtmlEvaluationMessage> evaluate(Level level, String html)
	{
		// Creates the tidy object that will be evaluate the content.
		Tidy tidy = new Tidy();
		// sets the listener tha will be store the messages founded for
		// the content.
		HtmlEvaluationTidyMessageListener listener = new HtmlEvaluationTidyMessageListener();
		tidy.setMessageListener(listener);
		tidy.setInputEncoding("UTF-8");
		// creates a fake output to avoid that the messages found, for the
		// tidy, are printed on console.
		OutputStream os = new OutputStream()
		{
			@Override
			public void write(int b) throws IOException
			{
			}
		};
		PrintWriter output = new PrintWriter(os);
		// sets the fake output
		tidy.setErrout(output);
		StringReader reader = new StringReader(html);
		// makes the evaluation
		tidy.parse(reader, new StringWriter());
		// extract and return the messages found for the content.
		return extractResultMessages(level, listener);
	}

	/**
	 * Extract the messages from the listener from Tidy Object.
	 * 
	 * @param level
	 *            minimal level of the messages to be considered.
	 * @param listener
	 *            listener from the Tidy object.
	 * @return list of messages founded on listener based on the minimal level
	 *         defined on level parameter.
	 * @since 6.0.0
	 */
	private List<HtmlEvaluationMessage> extractResultMessages(Level level, HtmlEvaluationTidyMessageListener listener)
	{
		List<HtmlEvaluationMessage> result = new ArrayList<HtmlEvaluationMessage>();
		if (level != null)
		{
			for (HtmlEvaluationMessage htmlValidationMessage : listener.getMessages())
			{
				// checks the minimal level to be considered.
				if (htmlValidationMessage.getLevel().compareTo(level) >= 0)
				{
					result.add(htmlValidationMessage);
				}
			}
		}
		else
		{
			return listener.getMessages();
		}
		return result;
	}

}